1 /*
2 Copyright: Marcelo S. N. Mancini (Hipreme|MrcSnm), 2018 - 2021
3 License:   [https://creativecommons.org/licenses/by/4.0/|CC BY-4.0 License].
4 Authors: Marcelo S. N. Mancini
5 
6 	Copyright Marcelo S. N. Mancini 2018 - 2021.
7 Distributed under the CC BY-4.0 License.
8    (See accompanying file LICENSE.txt or copy at
9 	https://creativecommons.org/licenses/by/4.0/
10 */
11 module hip.util.reflection;
12 
13 /**
14 *   Used when wanting to represent any struct compatible with a static array.
15 */
16 bool isTypeArrayOf(Type, Array, int Count)()
17 {
18     static if(is(Array == Type[Count]))
19         return true;
20     else static if(!is(Array == struct))
21         return false;
22     else
23     {
24         int count = 0;
25   		static foreach(v; Array.tupleof)
26     		static if(is(typeof(v) == Type))
27       			count++;
28             else static if(__traits(isStaticArray, typeof(v)) && is(typeof(v.init[0]) == Type))
29             	count+= v.length;
30   		return count == Count;
31     }
32 }
33 
34 template isDynamicArray(T)
35 {
36     static if (is(T == U[], U))
37         enum bool isDynamicArray = true;
38     else static if (is(T U == enum))
39         // BUG: isDynamicArray / isStaticArray considers enums
40         // with appropriate base types as dynamic/static arrays
41         // Retain old behaviour for now, see
42         // https://github.com/dlang/phobos/pull/7574
43         enum bool isDynamicArray = isDynamicArray!U;
44     else
45         enum bool isDynamicArray = false;
46 }
47 
48 enum bool isArray(T) = isDynamicArray!T || __traits(isStaticArray, T);
49 
50 /**
51  * Detect whether type `T` is a pointer.
52  */
53 enum bool isPointer(T) = is(T == U*, U);
54 
55 /**
56  * Detect whether `T` is a built-in numeric type (integral or floating
57  * point).
58  */
59 template isNumeric(T)
60 {
61     static if (!__traits(isArithmetic, T))
62         enum isNumeric = false;
63     else static if (__traits(isFloating, T))
64         enum isNumeric = is(T : real); // Not __vector, imaginary, or complex.
65     else static if (is(T U == enum))
66         enum isNumeric = isNumeric!U;
67     else
68         enum isNumeric = __traits(isZeroInit, T) // Not char, wchar, or dchar.
69             && !is(immutable T == immutable bool) && !is(T == __vector);
70 }
71 
72 
73 
74 ///Copy pasted from std.traits for not importing too many things
75 template isFunction(X...)
76 if (X.length == 1)
77 {
78     static if (is(typeof(&X[0]) U : U*) && is(U == function) ||
79                is(typeof(&X[0]) U == delegate))
80     {
81         // x is a (nested) function symbol.
82         enum isFunction = true;
83     }
84     else static if (is(X[0] T))
85     {
86         // x is a type.  Take the type of it and examine.
87         enum isFunction = is(T == function);
88     }
89     else
90         enum isFunction = false;
91 }
92 
93 template getParams (alias fn) 
94 {
95 	static if ( is(typeof(fn) params == __parameters) )
96     	alias getParams = params;
97 }
98 alias Parameters = getParams;
99 enum hasModule(string modulePath) = (is(typeof((){mixin("import ", modulePath, ";");})));
100 enum hasType(string TypeName) = __traits(compiles, {mixin(TypeName, " _;");});
101 
102 template isEnum(alias s)
103 {
104     static if(is(s == enum))
105         enum bool isEnum = true;
106     else
107         enum bool isEnum = false;
108 }
109 
110 
111 template getUDAs(alias symbol)
112 {
113     enum getUDAs = __traits(getAttributes, symbol);
114 }
115 
116 template hasUDA(alias symbol, alias UDA)
117 {
118     enum helper = ()
119     {
120         bool ret = false;
121         foreach(att; __traits(getAttributes, symbol))
122         {
123             static if(is(typeof(UDA)))
124             {
125                 if(att == UDA) ret = true;
126             }
127             else
128             {
129                 if(is(typeof(att) == UDA) || is(att == UDA)) ret = true;
130             } 
131         }
132         return ret;
133     }();
134     enum hasUDA = helper;
135 }
136 template isReference(T)
137 {
138     enum isReference = is(T == class) || is(T == interface);
139 }
140 template hasMethod(T, string method, Params...)
141 {
142     enum hasMethod = __traits(hasMember, T, method) && is(getParams!(__traits(getMember, T, method)) == Params);
143 }
144 
145 
146 
147 size_t enumLength(T)()
148 if(is(T == enum))
149 {
150     return __traits(allMembers, T).length;
151 }
152 
153 
154 /**
155 * Used on: 
156 *   - Static Methods
157 *   - Class Names
158 *   Used in conjunction to HipExportDFunctions.
159 *   You may specify a suffix, if you so, `_suffix` is added
160 *   ExportD will do nothing to static methods when building release. However, it will still produce
161 *   a function for returning a new class.
162 */
163 struct ExportD{string suffix;}
164 
165 
166 /**
167 *   Will basically generate an export name such as className_funcSymbol
168 *   If it has ExportD with a suffix, it will be basically className_funcSymbol(suffix)
169 */
170 template generateExportName(string className, alias funcSymbol)
171 {
172 	//Means that it has a suffix
173 	static if(is(typeof(__traits(getAttributes, funcSymbol)[0]) == ExportD))
174 		enum generateExportName = className~"_"~__traits(identifier, funcSymbol)~"_"~__traits(getAttributes, funcSymbol)[0].suffix;
175 	else
176 		enum generateExportName = className~"_"~__traits(identifier, funcSymbol);
177 }
178 
179 /**
180 *   Returns a code to be mixed in.
181 *   If isRef, it will call with hipSaveRef for not being colled
182 *   funcCallCode can be anything as `className.functionName` or even `new Class`
183 */
184 private string getExportedFuncImpl(bool isRef, string funcCallCode)
185 {
186     assert(__ctfe);
187     string ret;
188     if(isRef)
189     {
190         ret = q{
191             import hip.util.lifetime;
192             return cast(typeof(return))hipSaveRef(cast(Object)
193         } ~ funcCallCode~"(__traits(parameters)));}";
194     }
195     else
196     {
197         ret = "return "~funcCallCode~"(__traits(parameters));}";
198     }
199     return ret;
200 }
201 
202 
203 ///ClassT, Ctor, string className
204 ///This class MUST have an interface, because it will bug out when calling the function with `need opCmp for class`
205 template generateExportConstructor(ClassT, string className)
206 {
207     enum impl = ()
208     {
209         return "export extern(System) I" ~ className ~ " new" ~ className ~ //export extern(System) Class newClass
210         "(getParams!(__traits(getMember, Class, \"__ctor\"))){"~ //(A a, B b...)
211         getExportedFuncImpl
212         (
213             true, //IsReference
214             "new "~className
215         );
216     }();
217 
218     enum generateExportConstructor = impl;
219 }
220 
221 /**
222 *   It will create a `export extern(System)` function, thus, making it a C callable code.
223 *   This function comes from a static method, and has special code injection for making the
224 *   GC not collect if it is an object
225 */
226 template generateExportFunc(string className, alias funcSymbol)
227 {
228     import std.traits:ReturnType;
229     enum impl = ()
230     {
231         assert(__ctfe);
232 
233         alias RetType = ReturnType!funcSymbol;
234         string ret = "export extern(System) "~RetType.stringof~" "~generateExportName!(className, funcSymbol);
235         ret~= "(getParams!(sym)){";
236 
237         ret~= getExportedFuncImpl
238         (
239             isReference!(RetType),
240             className~"."~__traits(identifier, funcSymbol)
241         );
242 
243         return ret;
244     }();
245 
246     enum generateExportFunc = impl;
247 }
248 
249 
250 struct Version
251 {
252     template opDispatch(string s)
253     {
254         mixin(`version(`~s~`)enum opDispatch=true;else enum opDispatch=false;`);
255     }
256 }
257 
258 
259 
260 /**
261 *   Intermediary step for getting an alias to the Class type
262 *   The difference with HipExportDFunctionsImpl is that it does not generate
263 *   Static method output when not in script version.
264 */
265 mixin template HipExportDFunctionsImpl(string className, Class)
266 {
267     //If the class has ExportD, it will export a function called new(ClassName)
268     //It can't contain more than one constructor.
269     static if(hasUDA!(Class, ExportD))
270     {
271         static assert(
272             __traits(getOverloads, Class, "__ctor").length == 1, 
273             "Can't export class with more than one constructor ("~className~")"
274         );
275         mixin(
276             generateExportConstructor!(Class, className)
277         );
278         pragma(msg, "Exported Class "~className);
279     }
280     version(Load_DScript)
281     {
282         //Get all static methods that has ExportD
283         static foreach(sym; getSymbolsByUDA!(Class, ExportD))
284         {
285             static if(!is(sym == Class))
286             {
287                 //Assert that the symbol to generate does not exists yet
288                 static assert(__traits(compiles, mixin(generateExportName!(className, sym))),
289                 "ExportD '" ~ generateExportName!(className, sym) ~
290                 "' is not unique, use ExportD(\"SomeName\") for overloading with a suffix");
291 
292                 pragma(msg, "Exported "~(generateExportName!(className, sym)));
293                 //Func signature
294                 //Check if it is a non value type
295                 mixin(generateExportFunc!(className, sym));
296             }
297         }
298     }
299 }
300 
301 /**
302 *   Iterates through a module and generates `export` function declaration for each
303 *   @ExportD function found on it.
304 *   If the class itself is @ExportD, it will create a method new(ClassName) to be exported too
305 *   *   The difference with HipExportDFunctions is that it does not generate
306 *   *   Static method output when not in script version.
307 */
308 mixin template HipExportDFunctions(alias mod)
309 {
310 	import std.traits:getSymbolsByUDA;
311     pragma(msg, "Exporting ", mod.stringof);
312 	static foreach(mem; __traits(allMembers, mod))
313 	{
314         //Currently only supported on classes and structs
315 		static if( (is(__traits(getMember, mod, mem) == class) || is(__traits(getMember, mod, mem) == struct) ))
316 		{
317             mixin HipExportDFunctionsImpl!(mem, __traits(getMember, mod, mem));
318 		}
319 	}
320 }
321 
322 
323 string attributes(alias member)() 
324 {
325     assert(!__ctfe);
326 
327 	string ret;
328 	foreach(attr; __traits(getFunctionAttributes, member))
329 		ret~= attr ~ " ";
330 	return ret;
331 }
332 
333 
334 
335 
336 template hasOverload(T,string member, OverloadType)
337 {
338     bool impl()
339     {
340         if(!__ctfe)
341             return false;
342         bool ret = false;
343         static foreach(ov; __traits(getVirtualMethods, T, member))
344             static if(is(typeof(ov) == OverloadType))
345                 ret = true;
346         return ret;
347     }
348 
349     enum hasOverload = impl;
350 }
351 
352 
353 bool isMethodImplemented(T, string member, FuncType)()
354 {
355     if(!__ctfe)
356         return false;
357     bool ret;
358     static foreach(overload; __traits(getVirtualMethods, T, member))
359         if(is(typeof(overload) == FuncType) && !__traits(isAbstractFunction, overload))
360             ret = true;
361     return ret;
362 }
363 
364 
365 /** 
366  * Private to forward interface
367  */
368 string ForwardFunc(alias func, string funcName, string member)()
369 {
370     if(!__ctfe) return null;
371 
372     return attributes!func~ " ReturnType!(ov) " ~ funcName ~ "(getParams!(ov))"~
373          "{ return " ~ member ~ "." ~funcName ~ "(__traits(parameters));}";
374 }
375 
376 mixin template ForwardInterface2(string member, I) if(is(I == interface))
377 {
378     import hip.util.reflection:isMethodImplemented, ForwardFunc;
379 
380     static assert(is(typeof(mixin(member)) : I),
381         "For forwarding the interface, the member "~member~" should be or implement "~I.stringof
382     );
383 
384     static foreach(m; __traits(allMembers, I)) 
385     static foreach(ov; __traits(getVirtualMethods, I, m))
386     {
387         //Check for overloads here
388         static if(!isMethodImplemented!(typeof(this), m, typeof(ov)))
389             mixin(ForwardFunc!(ov, m, member));
390     }
391 }
392 
393 
394 
395 void GenerateGetterSettersInterfaceImpl(interface_)()
396 {
397     if(!__ctfe) return;
398 
399     import hip.util.array;
400     foreach(mem; __traits(allMembers, interface_))
401     {
402         alias member = __traits(getMember, interface_, mem);
403         if(__traits(isFinalFunction, member))
404             continue;
405         auto attributes = __traits(getFunctionAttributes, member);
406         if(attributes.contains("ref"))
407             continue;
408     }
409 
410 }
411 
412 /**
413 *   Generates getter and setter for given interface.
414 *   - Final methods are excluded.
415 *   - `void` return types are excluded
416 *   - `const` methods will only generate the const getter
417 */
418 mixin template GenerateGettersSettersInterface(interface_) if(is(interface_ == interface))
419 {
420     static foreach(mem; __traits(allMembers, interface_))
421     {
422 
423     }
424 }
425 
426 /**
427 * This mixin is able to generate runtime accessors. That means that by having a string, it is
428 *possible to modify 
429 */
430 mixin template GenerateRuntimeAccessors()
431 {
432     T* getProperty(T)(string prop)
433     {
434         alias T_this = typeof(this);
435 
436         switch(prop)
437         {
438             static foreach(member; __traits(allMembers, T_this))
439             {
440                 static if(is(typeof(__traits(getMember, T_this, member)) == T))
441                 {
442                     case member:
443                         return &__traits(getMember, T_this, member);
444                 }
445             }
446             default:
447                 return null;
448         }
449     }
450 
451     void setProperty(T)(string propName, T value)
452     {
453         T* prop = getProperty!T(propName);
454         if(prop !is null)
455             *prop = value;
456     }
457 }